;=================================================
; an experience w/functions
;
; :DOC: A standard function's definition
;
; (defun name (parameter*)
;  "Optional documentation string."
;  body-form*)
;
; LISP naming style:
; this-is-a-function (use hyphens rather than underscores or inner caps)
;


;=================================================
; sample 1
;
(defun test-function1 ()
 "this is a `test-function1` function"
 (if t nil)
 )

;=================================================
; sample 2
;
(defun verbose-sum (x y)
  "Sum any two numbers after printing a message."
  (format t "Summing ~d and ~d.~%" x y)
  (+ x y))

;=================================================
; sample 3
; optional parameter's demonstration
;
; :DOC: If the arguments run out before the optional parameters do, the remaining optional parameters are bound to the value NIL
(defun foo (a b &optional c d) (list a b c d))

;=================================================
; sample 4
; making a rectangle
;
(defun make-rectangle (width &optional (height width))
 (format t "make-rectangle(~a, ~a)~%" width height))

;=================================================
; sample 5
; using supplied-p in optional parameters
;
(defun foo2 (a b &optional (c 3 c-supplied-p))
  (list a b c c-supplied-p))

;=================================================
; sample 6
; `rest' parameters.
; Examples of the rest parameters:
; (defun format (stream string &rest values) ...)
; (defun + (&rest numbers) ...)
; 
(defun test-rest (mandatory-parameter &rest optional-parameters-list)
 (format t "test-rest:~%mandatory-parameter = ~a~%the length of optional parameters list is ~d~%"
  mandatory-parameter (length optional-parameters-list))
 (dolist (i optional-parameters-list)
  (format t "   param ~a~%" i)
  )
 (format t "=========~%")
 )

;=================================================
; sample 7
; keyword parameters
;
(defun keyword-test (non-key-param &key key-param-1 key-param-2 key-param3)
 (format t "keyword-test:~%non-key-param = ~a~%key-param-1 = ~a~%key-param-2 = ~a~%key-param-3 = ~a~%=====~%"
  non-key-param key-param-1 key-param-2 key-param3)
 )

;=================================================
; sample 8
; more complicated `keyword' example
;
(defun keyword-test-2 (&key (a 0) (b 0 b-supplied-p) (c (+ a b)))
  (format t "keyword-test-2:~%a = ~a~%b = ~a(supplied = ~a)~%c = ~a~%=====~%"
   a b b-supplied-p c)
  )

;=================================================
; sample 9
; Another `keyword' example. Differing keyword parameters names
; from the names of variables
(defun keyword-test-3 (&key ((:apple a)) ((:box b) 0) ((:charlie c) 0 c-supplied-p))
  (list a b c c-supplied-p))
; - lets the caller call it like this:
; (foo :apple 10 :box 20 :charlie 30) ==> (10 20 30 T)


;=================================================
; sample 10
; mixing parameters
(defun keyword-rest-test (&rest rest &key a b c) (list rest a b c))
;you get this result:
;(keyword-rest-test :a 1 :b 2 :c 3)  ==> ((:A 1 :B 2 :C 3) 1 2 3)

;=================================================
; sample 11
; return-from test
(defun retfrom-test (n)
  (dotimes (i 10)
    (dotimes (j 10)
      (when (> (* i j) n)
        (return-from retfrom-test (list i j))))))

;=================================================
; sample 12
; function objects
(defun test-function-objects ()

 ; define a function
 (defun foo (x y) (* x y 2))

 ; #' is identical to (function ...) call
 (format t "func object identity: ~a, ~a~%" (function foo) #'foo)

 ; the following calls are identical
 (format t "calls to foo: ~a, ~a, ~a~%"
  (foo 10 20)
  (funcall (function foo) 10 20)
  (funcall #'foo 10 20)
  )

 ; building a plot
 (defun plot (fn min max step)
  (loop for i from min to max by step do
   (loop repeat (funcall fn i) do (format t "*"))
   (format t "~%") ; end of line
   ) ; loop
  )
 ; building a plot for exponenta:
 (plot #'exp 0 4 1/2)

 ; using apply
 (defun testfn (a b c)
  (format t "testfn called: a = ~a, b = ~a, c = ~a~%" a b c))

 (setq arg-list (list 1 2 3))
 (apply #'testfn arg-list)

 (setq arg-list-2 (list 2 3))
 (apply #'testfn 1 arg-list-2)

 ; using anonymous function aka `lambda'
 (funcall
    #'(lambda (x y)
        (format t "anonymous func: x = ~a, y = ~a~%" x y))
    1 2)

 ; one another `lambda' sample
 (plot #'(lambda (x) (* 2 x)) 0 4 1)

 ) ; test-function-objects

;=================================================
; testing a defined functions
;
(defun test ()

 ; 1
 ;(test-function1)

 ; 2
 ;(verbose-sum 12 5)

 ; 3
 ;(format t "~a~%" (foo 1 2 3))

 ; 4
 ;(make-rectangle 12)
 ;(make-rectangle 13 15)

 ; 5
 ;(format t "5-1: ~a~%5-2: ~a~%5-3: ~a~%" (foo2 1 2) (foo2 1 2 3) (foo2 1 2 4) )

 ; 6
 ;(test-rest 1)
 ;(test-rest 1 2)
 ;(test-rest 1 2 3 4 5)

 ; 7
 ;(keyword-test 1)
 ;(keyword-test 1 :key-param-1 2)
 ;(keyword-test 1 :key-param-2 3 :key-param-1 2)

 ; 8
 ;(keyword-test-2)
 ;(keyword-test-2 :a 12)
 ;(keyword-test-2 :a 10 :c 5)
 ;(keyword-test-2 :a 5 :b 6)

 ; 9
 ;(format t "~a~%" (keyword-test-3 :apple 'Apple :box 12 :charlie (list 'A 1 2)))

 ; 10
 ;(format t "keyword-rest-test: ~a~%" (keyword-rest-test :a 1 :c 300 :b 20) )

 ; 11
 ;(format t "retfrom-test: ~a, ~a~%" (retfrom-test 19) (retfrom-test 9999))

 ; 12
 (test-function-objects)

 ; 13 documentation test
 (format t "doc test: `~a`~%" (documentation 'verbose-sum 'function))
 )

;=================================================
; a program's entry point
;
(defun main ()

 ; wellcome :)
 (format t "A LISP's functions sample.~%=====~%")

 (test)

 ; leave the application
 (exit)
 )

; proceed to the entry point
(main)
